﻿using System;
using System.Diagnostics;
using System.Threading;
using Less.Tools;
using Less.Core.Model;

namespace Less.Core.Loggers
{
    /// <summary>
    /// Base class to create loggers
    /// </summary>
    public abstract class BaseLogger : Log.ILogger
    {
        private EntrySeverities _minSeverity = EntrySeverities.Debug;
        #region ILogger Members
        /// <summary>
        /// Minimal Severity for this Logger. Wraper for the config
        /// </summary>
        public EntrySeverities MinSeverity { get { return this._minSeverity; } set { this._minSeverity = value; } }

        /// <summary>
        /// Basic writer method. 
        /// <remarks> Will retry whe writing whe fails</remarks>
        /// </summary>
        /// <param name="entrySeverity"></param>
        /// <param name="entryMessage"></param>
        /// <param name="entryCategory"></param>
        /// <param name="source"></param>
        /// <param name="userName"></param>
        /// <param name="processId"></param>
        /// <param name="metricName"></param>
        /// <param name="metricValue"></param>
        /// <returns></returns>
        public virtual bool Write(EntrySeverities entrySeverity, string entryMessage, EntryCategories entryCategory = EntryCategories.Base, string source = "", string userName = "", long processId = 0, string metricName = "", object metricValue = null)
        {
            string message = ComposeMessage(entrySeverity, entryMessage, entryCategory, source, userName, processId, metricName, metricValue);
            return WriteMessage(message);
        }

        /// <summary>
        /// Basic message composer
        /// </summary>
        /// <param name="entrySeverity"></param>
        /// <param name="entryMessage"></param>
        /// <param name="entryCategory"></param>
        /// <param name="source"></param>
        /// <param name="userName"></param>
        /// <param name="processId"></param>
        /// <param name="metricName"></param>
        /// <param name="metricValue"></param>
        /// <returns></returns>
        public virtual string ComposeMessage(EntrySeverities entrySeverity, string entryMessage, EntryCategories entryCategory, string source = "", string userName = "", long processId = 0, string metricName = "", object metricValue = null)
        {
            LogEntry logEntry = LogEntry.CreateLogEntry(entrySeverity, entryMessage, entryCategory, source, userName, processId, metricName, metricValue);
            string template = string.Format("{0} | {1} | {2} | {3} | {4} | {5} | {6} | {7} | {8} | {9} ",
                                            FormatTimeStamp(logEntry.Created),
                                            FormatSeverity(logEntry.Severity),
                                            FormatCategory(logEntry.Category),
                                            Thread.CurrentThread.ManagedThreadId.ToString().FixedLenght(2),
                                            logEntry.Source.FixedLenght(20),
                                            logEntry.Message, logEntry.MetricName, logEntry.MetricValue, logEntry.ProcessId, logEntry.UserName);
            return template;
        }

        /// <summary>
        /// The real writer, empty by default
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public virtual bool WriteMessage(string message)
        {
            return true;
        }
        #endregion
        #region formatters
        public virtual string FormatTimeStamp(DateTime timeStamp)
        {
            return timeStamp.ToString("HH:mm:ss");
        }

        public virtual string FormatSeverity(EntrySeverities logSeverity)
        {
            switch (logSeverity)
            {
                case EntrySeverities.Debug:
                    return "Debug:";
                case EntrySeverities.Info:
                    return "Info:";
                case EntrySeverities.Warning:
                    return "Warnig:";
                case EntrySeverities.Metric:
                    return "Metric:";
                case EntrySeverities.Error:
                    return "Error:";
                case EntrySeverities.Fatal:
                    return "FATAL:";
                default:
                    return "";
            }
        }

        public virtual string FormatCategory(EntryCategories logCategory)
        {
            return logCategory == EntryCategories.Base ? "" : logCategory.ToString();
        }
        #endregion
    }
       
       
    /// <summary>
    /// Logger that writes to the main console
    /// </summary>
    public class ConsoleLogger : BaseLogger
    {
        public ConsoleLogger()
        {
            MinSeverity = EntrySeverities.Info;
        }

        /// <summary>
        /// Write once 
        /// </summary>
        /// <param name="entrySeverity"></param>
        /// <param name="entryMessage"></param>
        /// <param name="entryCategory"></param>
        /// <param name="entrySource"></param>
        /// <param name="userName"></param>
        /// <param name="processId"></param>
        /// <param name="metricName"></param>
        /// <param name="metricValue"></param>
        /// <returns></returns>
        public override bool Write(EntrySeverities entrySeverity, string entryMessage, EntryCategories entryCategory = EntryCategories.Base, string entrySource = "", string userName = "", long processId = 0, string metricName = "", object metricValue = null)
        {
            string message = ComposeMessage(entrySeverity, entryMessage, entryCategory, entrySource, userName, processId, metricName, metricValue);
            WriteMessage(message);
            return true;
        }

        public override string ComposeMessage(EntrySeverities entrySeverity, string entryMessage, EntryCategories entryCategory, string entrySource = "", string userName = "", long processId = 0, string metricName = "", object metricValue = null)
        {
            LogEntry logEntry = LogEntry.CreateLogEntry(entrySeverity, entryMessage, entryCategory, entrySource, userName, processId, metricName, metricValue);
            string template = string.Format("{0} {1} {2} ", FormatSeverity(logEntry.Severity), FormatCategory(logEntry.Category), logEntry.Message);
            return template;
        }

        /// <summary>
        /// Writes a string to the console
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public override bool WriteMessage(string message)
        {
            Console.WriteLine(message);
            return true;
        }
    }
    /// <summary>
    /// Logger that writes to system diagnostics trace
    /// </summary>
    public class TraceLogger : BaseLogger
    {
        public override bool Write(EntrySeverities entrySeverity, string entryMessage, EntryCategories entryCategory = EntryCategories.Base, string entrySource = "", string userName = "", long processId = 0, string metricName = "", object metricValue = null)
        {
            bool ok = true;
            string message = ComposeMessage(entrySeverity, entryMessage, entryCategory, entrySource, userName, processId, metricName, metricValue);
            switch (entrySeverity)
            {
                case EntrySeverities.Debug:
                    break;
                case EntrySeverities.Info:
                    Trace.TraceInformation(message);
                    break;
                case EntrySeverities.Warning:
                    Trace.TraceWarning(message);
                    break;
                case EntrySeverities.Metric:
                    break;
                case EntrySeverities.Error:
                    Trace.TraceError(message);
                    break;
                case EntrySeverities.Fatal:
                    Trace.TraceError(message);
                    break;
                default:
                    break;
            }
            return ok;
        }
    }
}
